package com.haier.utils;

import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 导出后缀为 .xlsx 的Excel表格 <br>
 * 
 * 使用说明: <br>
 * 1.使用有参构造方法new一个本类对象,入参都是必须的,请参照有参构造上的说明进行相关参数配置; <br>
 * 2.通过set方法对可选参数进行配置(例如设置页名称,标题,日期格式等),如不设置,将会采用默认值(具体可参照对应set方法说明); <br>
 * 3.调用createXlsx()方法完成导出
 * 
 * @author DawnXia
 *
 */
public class XlsxExcelCreation<T> {

	protected SXSSFWorkbook wb;
	/** 标题样式 */
	protected CellStyle titleCellStyle; 
	/** 表头样式 */
	protected CellStyle headCellStyle; 
	/** 主体样式 */
	protected CellStyle bodyCellStyle; 
	protected SimpleDateFormat dateFormat;
	/** 记录将要操作的行位置 */
	protected int nextRow = 0; 
	/** 标题内容 */
	protected String titleName = ""; 
	/** 标题高度 */
	protected int titleHeight = 500; 

	protected String sheetName = "Sheet1";
	protected String replaceStr = "/";
	protected String datePattern = "yyyy-MM-dd HH:mm:ss";

	protected List<T> dataList;
	/** 列宽 */
	protected int[] columnWidth; 
	/** 表头内容 */
	protected String[][][] headNames; 
	/** 字段内容 */
	protected String[][] valueName;
	protected OutputStream outputStream;

	protected XlsxExcelCreation() {
		//创建 SXSSFWorkbook 对象
		wb = new SXSSFWorkbook(1000);
		//设置表头样式
		this.setHeadCellStyle();
		//设置通用样式
		this.setBodyCellStyle();
	}

	/**
	 * 设置导出Excel <br>
	 * 参数说明:<br>
	 * 	columnWidth 列宽度 对应每一列的宽度 1000的宽度约为2个汉字的宽度<br>
	 * 		例:{3000,6000,4000...} <br>
	 * 	headNames 列名称 支持向右,向下合并单元格 此处使用3维数组,最外层为第几行,第2层为第几列,最里层为列名称及参数<br>
	 * 		如果要合并行,只需要将后面需要合并的行名称设置为""即可 {"name"},{""} <br>
	 * 		如果要合并列,需要在最里层数组列名称后添加要合并的行数,被合并的位置列名设置为"null"字符串{{"name","1"}},{{"null}} <br>
	 * 		例:{{{"ID","1"},{"name"},{""}},{{"null"},"name1","name2"}} <br>
	 * 	valueName 填充内容 此处需对应 T 里的属性名(不区分私有,公有),此处使用2维数组,方便拓展对数据的处理 <br>
	 * 		在最里层数组,属性名后(arr[i][1]),添加"null"则会对改属性的值进行空处理,替换指定的字符,其他功能待拓展<br>
	 * 		Date 类型的数据会自动格式化,如果需要自定义格式,可调用 setDatePattern 方法设置 <br>
	 * 		例:{{"id"},{"name","null"},{"date"}....} <br>
	 * 
	 * 注意:以上数组列级的length应该是相等的<br>
	 * 
	 * @param dataList 数据
	 * @param outputStream 输出流
	 * @param columnWidth 列宽度
	 * @param headNames 列名称
	 * @param valueName 填充内容
	 */
	public XlsxExcelCreation(List<T> dataList, OutputStream outputStream, int[] columnWidth, String[][][] headNames,
                             String[][] valueName) {
		this();
		this.dataList = dataList;
		this.columnWidth = columnWidth;
		this.headNames = headNames;
		this.valueName = valueName;
		this.outputStream = outputStream;
	}

	/**
	 * 创建Xlsx
	 * @throws Exception
	 */
	public void createXlsx() throws Exception {

		Sheet sheet = wb.createSheet(sheetName);
		//设置列宽
		this.setColumnWidth(sheet);
		//如果标题名称不为空则说明设置了标题
		if(titleName != null && !titleName.isEmpty()) {
			//设置标题
			this.createExcelTitle(sheet);
		}
		// 生成表头
		this.createExcelHead(sheet);
		// 生成表格主体内容
		this.createExcelBody(sheet);
		try {
			wb.write(outputStream);
		} finally {
			// 关闭资源
			try {
				outputStream.close();							
			} finally {
				wb.dispose();				
			}
		}
		
	}

	/**
	 * 设置页名称,默认"Sheet1"
	 * @param sheetName
	 */
	public void setSheetName(String sheetName) {
		this.sheetName = sheetName;
	}

	/**
	 * 设置空值替换内容,默认"/"
	 * @param replaceStr
	 */
	public void setReplaceStr(String replaceStr) {
		this.replaceStr = replaceStr;
	}

	/**
	 * 设置Date对象格式化样式,默认"yyyy-MM-dd HH:mm:ss"
	 * @param datePattern
	 */
	public void setDatePattern(String datePattern) {
		this.datePattern = datePattern;
	}
	
	/**
	 * 设置标题
	 * @param titleName 标题名称
	 * @param titleHeight 单元格高度
	 * @param fontHeight 字体大小
	 */
	public void setTitle(String titleName,int titleHeight, int fontHeight) {
		this.titleName = titleName;
		this.titleHeight = titleHeight;
		this.setTitleCellStyle(fontHeight);
	}
	
	/**
	 * 使用默认高度(500),默认字体大小(14)创建标题
	 * @param titleName
	 */
	public void setTitle(String titleName) {
		this.setTitle(titleName, titleHeight, 14);
	}

	/**
	 * 表格创建主体
	 * @param sheet
	 * @throws Exception 
	 */
	protected void createExcelBody(Sheet sheet) throws Exception {
		
		//初始化时间格式器
		dateFormat = new SimpleDateFormat(datePattern);
		
		for (T t : dataList) {
			Row row = sheet.createRow(nextRow);
			List<String> valueList = this.getValueList(t);
			for (int i = 0; i < valueList.size(); i++) {
				Cell cell = row.createCell(i);
				cell.setCellStyle(bodyCellStyle);
				cell.setCellValue(valueList.get(i));
			}

			nextRow++;
		}
	}

	/**
	 * 取单相关列的数据
	 * 
	 * @throws Exception 
	 */
	protected List<String> getValueList(T t) throws Exception {
		List<String> valueList = new ArrayList<String>();
		Class<?> clazz = t.getClass();
		for (int i = 0; i < valueName.length; i++) {
			Field field = clazz.getDeclaredField(valueName[i][0]);
			field.setAccessible(true);

			Object obj = field.get(t);
			Class<?> type = field.getType();
			String val = "";

			if (obj != null) {
				if (type.equals(Date.class)) {
					Date date = (Date) obj;
					val = dateFormat.format(date);
				} else {
					val = String.valueOf(field.get(t));
				}
			}
			if (valueName[i].length > 1) {
				if (Objects.equals(valueName[i][1], "null")) {
					val = this.emptyToNull(val);
				}
			}
			valueList.add(val);
		}
		return valueList;
	}
	
	/**
	 * 将null值和空值替换为指定字符,默认替换为"--"
	 */
	protected String emptyToNull(String str) {
		if (str == null || str.isEmpty()) {
			return replaceStr;
		} else {
			return str;
		}
	}

	/**
	 * 创建表头
	 */
	protected void createExcelHead(Sheet sheet) {

		//获取行
		for (int i = 0; i < headNames.length; i++) {
			Row row = sheet.createRow(nextRow);
			row.setHeight((short) 400);
			//获取列数
			int rowLen = headNames[i].length;
			//获取列
			for (int j = 0; j < rowLen; j++) {
				// 获取名称
				String headName = headNames[i][j][0];
				//如果列名为空,则不做处理
				if (headName != null && !headName.isEmpty() && !Objects.equals(headName, "null")) {
					//如果长度为2,则说明需要向下合并单元格
					if (headNames[i][j].length == 2) {
						//获取需要向下合并数量
						int rowNum = Integer.valueOf(headNames[i][j][1]);
						//向下合并
						sheet.addMergedRegion(new CellRangeAddress(nextRow, nextRow + rowNum, j, j));
					} else {
						//判断下一列内容,如果为空,则需要和本列合并,
						int temp = 0;
						for (int k = j + 1; k < rowLen; k++) {
							String nextHeadName = headNames[i][k][0];
							if ((nextHeadName == null || nextHeadName.isEmpty()) && !Objects.equals(nextHeadName, "null")) {
								temp++;
							} else {
								break;
							}
						}
						if (temp > 0) {
							//向后合并单元格
							sheet.addMergedRegion(new CellRangeAddress(nextRow, nextRow, j, j + temp));
						}
					}
					
				}else {
					headName = "";
				}
				
				//为该单元格设置样式,内容
				Cell cell = row.createCell(j);
				cell.setCellStyle(headCellStyle);
				cell.setCellValue(headName);
			}
			//将指针后移
			nextRow++;
			
		}
	}
	
	/**
	 * 创建标题
	 * @param sheet
	 */
	protected void createExcelTitle(Sheet sheet) {
		Row row = sheet.createRow(nextRow);
		row.setHeight((short) titleHeight);
		sheet.addMergedRegion(new CellRangeAddress(nextRow, nextRow, 0, columnWidth.length - 1));
		Cell cell = row.createCell(0);
		cell.setCellStyle(titleCellStyle);
		cell.setCellValue(titleName);
		
		//将指针后移
		nextRow++;
	}

	/**
	 * 设置列宽
	 * @param sheet
	 */
	protected void setColumnWidth(Sheet sheet) {
		for (int i = 0; i < columnWidth.length; i++) {
			sheet.setColumnWidth(i, columnWidth[i]);
		}
	}

	/**
	 * 设置标题栏样式
	 */
	protected void setTitleCellStyle(int fontHeight) {
		titleCellStyle = wb.createCellStyle();

		// 设置单元格字体样式
		XSSFFont font = (XSSFFont) wb.createFont();
		// 字体加粗
		font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);
		// 设置字体的大小
		font.setFontHeight(fontHeight);
		// 将字体添加到表格中去
		titleCellStyle.setFont(font);
		// 设置单元格居中对齐
		titleCellStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER);
		// 设置单元格垂直居中对齐
		titleCellStyle.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);

	}

	/**
	 * 设置内容主体单元格样式
	 */
	protected void setBodyCellStyle() {
		bodyCellStyle = wb.createCellStyle();
		// 设置自动换行
		bodyCellStyle.setWrapText(false);
		// 设置水平对齐方式
		bodyCellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION);
		// 设置垂直对齐方式
		bodyCellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
		// 设置单元格边框为细线条（上下左右）
		bodyCellStyle.setBorderLeft(XSSFCellStyle.BORDER_THIN);
		bodyCellStyle.setBorderBottom(XSSFCellStyle.BORDER_THIN);
		bodyCellStyle.setBorderRight(XSSFCellStyle.BORDER_THIN);
		bodyCellStyle.setBorderTop(XSSFCellStyle.BORDER_THIN);
	}

	/**
	 * 设置表头单元格样式
	 */
	protected void setHeadCellStyle() {
		headCellStyle = wb.createCellStyle();
		Font font = wb.createFont();
		// 字体增粗
		font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); 
		// 白色
		font.setColor(HSSFColor.WHITE.index);
		headCellStyle.setFont(font);
		headCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.index);
		headCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
		headCellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		// 设置自动换行
		headCellStyle.setWrapText(true);
		// 设置垂直对齐方式
		headCellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
		// 设置单元格边框为细线条（上下左右）
		headCellStyle.setBorderLeft(XSSFCellStyle.BORDER_THIN);
		headCellStyle.setBorderBottom(XSSFCellStyle.BORDER_THIN);
		headCellStyle.setBorderRight(XSSFCellStyle.BORDER_THIN);
		headCellStyle.setBorderTop(XSSFCellStyle.BORDER_THIN);
		
	}

}
